#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/firmware.h>
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("TEST");
 
static dev_t devno;
 
#define KMAX_LEN 32
 
struct class *my_class;
struct device *my_device;
 
static struct firmware *fw_handle = NULL ;  
 
static int demo_open(struct inode *ind, struct file *fp)
{
	printk("demo open\n");
	return 0;
}
 
static int demo_release(struct inode *ind, struct file *fp)
{
	printk("demo release\n");
	return 0;
}
 
static ssize_t demo_read(struct file *fp, char __user *buf, size_t size, loff_t *pos)
{
	int rc;
	char kbuf[KMAX_LEN] = "read test\n"; 
	if (size > KMAX_LEN)
		size = KMAX_LEN;
	
	rc = copy_to_user(buf, kbuf, size);
	if(rc < 0) {
		return -EFAULT;
		pr_err("copy_to_user failed!");
	}
	return size;
}
 
static ssize_t demo_write(struct file *fp, const char __user *buf, size_t size, loff_t *pos)
{
	int rc;
	char kbuf[KMAX_LEN];
	if (size > KMAX_LEN)
		size = KMAX_LEN;
 
	rc = copy_from_user(kbuf, buf, size);
	if(rc < 0) {
		return -EFAULT;
		pr_err("copy_from_user failed!");
	}
	printk("%s", kbuf);
	return size;
}
 
static struct file_operations fops = {
	.open = demo_open,
	.release = demo_release,
	.read = demo_read,
	.write = demo_write,
};
 
static struct cdev cd;
 

#if 0

int request_firmware_nowait( struct module  *module , bool  uevent , const char  *name , struct device  *device , gfp_t  gfp , void  *context , void ( *cont )(const struct firmware *fw, void *context) )  

 参数

  struct module *module
  模块请求固件
  bool uevent
  如果此标志非零，则发送 uevent 以复制固件映像，否则必须手动完成固件复制。
  const char *name
  固件文件名
  struct device *device
  正在为其加载固件的设备
  gfp_t gfp
  分配标志
  void *context
  将传递给cont， 如果固件请求失败，则fw可能会传递。NULL
  void (*cont)(const struct firmware *fw, void *context)
  当固件请求结束时，函数将被异步调用

#endif

  /**
  * @brief    fml_firmware_config_cb
  * @param
  * @retval   none
  */
static void fml_firmware_config_cb(const struct firmware *cfg, void *ctx)
{
      int fw_error = 0;

      if(cfg)
      {
      	printk("%s\n", __func__);
      }
}

/**
* @brief    firmware update by file
* @param
* @retval 0:success, -1: fail,1:no need update
*/

int fml_fw_update_by_file(struct device*device)
{
      int ret_error = 0;

      ret_error = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, "my_firmware",
                                      device, GFP_KERNEL, NULL, fml_firmware_config_cb);

      if(ret_error)
      {
          printk("request_firmware_nowait failed:%d\n",ret_error);
          return -1;
      }
      printk("end\n");
      return 0;
}


/***

***/

static int demo_init(void)
{
	int rc, ret;
	rc = alloc_chrdev_region(&devno, 0, 1, "test2");
	if(rc < 0) {
		pr_err("alloc_chrdev_region failed!");
		return rc;
	}
	printk("MAJOR is %d\n", MAJOR(devno));
	printk("MINOR is %d\n", MINOR(devno));
 
	cdev_init(&cd, &fops);
	rc = cdev_add(&cd, devno, 1);
	if (rc < 0) {
		pr_err("cdev_add failed!");
		return rc;
	}
 
	my_class = class_create(THIS_MODULE, "mychr_dev0");
    
	if (!my_class)
    	{
        	printk(KERN_INFO "create calss failed\n");
        	//kobject_put(&my_char_cdev.kobj);
        	cdev_del(&cd);
        	unregister_chrdev_region(&cd,1);
        	return -1;
    	}
 
    	my_device = device_create(my_class, NULL, devno/*MAJOR(devno)*/,NULL,"chrdev");     /**/
    	if (!my_device)
    	{
        	printk(KERN_INFO "create device failed\n");
        	//kobject_put(&my_char_cdev.kobj);
        	cdev_del(&cd);
        	unregister_chrdev_region(&cd,1);
        	class_destroy(my_class);
        	return -1;
    	}
 
	if(!fml_fw_update_by_file(my_device))
		return 0;
	else
		goto delete_device;

delete_device:
	cdev_del(&cd);
	unregister_chrdev_region(devno, 1);
	device_del(my_device);
	printk(KERN_INFO "delete device /dev/my_char_dev \n");
	class_destroy(my_class);
	printk(KERN_INFO "delete device /sys/class/my_char_dev \n");
 
	return ret;

}
 
static void demo_exit(void)
{
    	release_firmware(fw_handle);  
    	printk(KERN_INFO "Released firmware\n");  
	cdev_del(&cd);
	unregister_chrdev_region(devno, 1);
	device_del(my_device);
	printk(KERN_INFO "delete device /dev/my_char_dev \n");
	class_destroy(my_class);
	printk(KERN_INFO "delete device /sys/class/my_char_dev \n");
 
	return;
}
 
module_init(demo_init);
module_exit(demo_exit);

